unit eclistframeunit;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, StdCtrls, database, fpjson, Types,
  ecstringiser, eccolourscheme, constants, LCLTranslator, Menus;

type

  { TECListFrame }

  TECListFrame = class(TFrame)
    EcListBox: TListBox;
    EcListPopupMenu: TPopupMenu;
    EditEcMenuItem: TMenuItem;
    AddNewEcMenuItem: TMenuItem;
    EcListSeparator1MenuItem: TMenuItem;
    CutEcMenuItem: TMenuItem;
    CopyEcMenuItem: TMenuItem;
    DeleteEcMenuItem: TMenuItem;
    EcListSeparator2MenuItem: TMenuItem;
    SelectAllEcsMenuItem: TMenuItem;
    PasteEcMenuItem: TMenuItem;
    procedure AddNewEcMenuItemClick(Sender: TObject);
    procedure CopyEcMenuItemClick(Sender: TObject);
    procedure EcListBoxClick(Sender: TObject);
    procedure EcListBoxDblClick(Sender: TObject);
    procedure EcListBoxDrawItem(Control: TWinControl; Index: Integer;
      ARect: TRect; State: TOwnerDrawState);
    procedure EcListBoxMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure EcListBoxSelectionChange(Sender: TObject; User: boolean);
    procedure EditEcMenuItemClick(Sender: TObject);
    procedure PasteEcMenuItemClick(Sender: TObject);
  private
    Db: TDatabase;
    Troop: TJSONObject;
    List: TJSONArray;
    ColourScheme: TEcColourScheme;
    Stringiser: TEcStringiser;
    LeaderWidth: Integer;

    SelectionStart, SelectionLength: Integer;

    function GetSelectionStart(MiddleIndex: Integer): Integer;
    function GetSelectionLength(StartingFrom: Integer): Integer;
    function IsEcSelected(Index: Integer): Boolean;

    function IsSingleEcSelected: Boolean;

    procedure EditSelectedCommand;
    procedure AddNewCommand;
    procedure SetUpPopupMenu;
  public
    procedure SetDatabase(ADb: TDatabase);
    procedure SetTroop(ATroop: TJSONObject);
    procedure SetList(AList: TJSONArray);
    procedure FillEventCommands;
    destructor Destroy; override;
  end;

  TStarterEcDesc = record
    ContinuationCode: Integer;
    StartCode: Integer;
  end;

  TEcContinuationType = (
    ectEat {< Take as many event commands of the specified type as possible,
              but only if they follow starter or each other directly},
    ectFind {< Find exactly one event command at the same indent, but probably
               separated by commands of other types},
    ectSpecial {< Used for 'Battle processing', which sometimes acts as ectFind}
  );

  TEnderEcDesc = record
    StartCode: Integer;
    EndCode: Integer;
    ContinuationType: TEcContinuationType;
  end;
  PEnderEcDesc = ^TEnderEcDesc;

const
  EC_INDENT = 10;
  INDENT_WIDTH = 20;

  StarterEcs: array [1..16] of TStarterEcDesc = (
    (ContinuationCode: SHOW_MESSAGE_LINE_EC_CODE; StartCode: SHOW_MESSAGE_START_EC_CODE),
    (ContinuationCode: SHOW_CHOICES_BRANCH_EC_CODE; StartCode: SHOW_CHOICES_START_EC_CODE),
    (ContinuationCode: SHOW_CHOICES_CANCEL_BRANCH_EC_CODE; StartCode: SHOW_CHOICES_START_EC_CODE),
    (ContinuationCode: SHOW_CHOICES_END_EC_CODE; StartCode: SHOW_CHOICES_START_EC_CODE),
    (ContinuationCode: SCROLLING_TEXT_LINE_EC_CODE; StartCode: SCROLLING_TEXT_START_EC_CODE),
    (ContinuationCode: COMMENT_CONTINUATION_EC_CODE; StartCode: COMMENT_EC_CODE),
    (ContinuationCode: CONDITIONAL_BRANCH_ELSE_EC_CODE; StartCode: CONDITIONAL_BRANCH_IF_EC_CODE),
    (ContinuationCode: CONDITIONAL_BRANCH_END_EC_CODE; StartCode: CONDITIONAL_BRANCH_IF_EC_CODE),
    (ContinuationCode: LOOP_END_EC_CODE; StartCode: LOOP_EC_CODE),
    (ContinuationCode: SET_MOVEMENT_ROUTE_SUBCOMMAND_EC_CODE; StartCode: SET_MOVEMENT_ROUTE_EC_CODE),
    (ContinuationCode: BATTLE_PROCESSING_WIN_EC_CODE; StartCode: BATTLE_PROCESSING_EC_CODE),
    (ContinuationCode: BATTLE_PROCESSING_ESCAPE_EC_CODE; StartCode: BATTLE_PROCESSING_EC_CODE),
    (ContinuationCode: BATTLE_PROCESSING_LOSE_EC_CODE; StartCode: BATTLE_PROCESSING_EC_CODE),
    (ContinuationCode: BATTLE_PROCESSING_END_EC_CODE; StartCode: BATTLE_PROCESSING_EC_CODE),
    (ContinuationCode: SHOP_PROCESSING_LINE_EC_CODE; StartCode: SHOP_PROCESSING_EC_CODE),
    (ContinuationCode: SCRIPT_LINE_EC_CODE; StartCode: SCRIPT_EC_CODE)
  );

  EnderEcs: array [1..10] of TEnderEcDesc = (
     (StartCode: SHOW_MESSAGE_START_EC_CODE; EndCode: SHOW_MESSAGE_LINE_EC_CODE; ContinuationType: ectEat),
     (StartCode: SHOW_CHOICES_START_EC_CODE; EndCode: SHOW_CHOICES_END_EC_CODE; ContinuationType: ectFind),
     (StartCode: SCROLLING_TEXT_START_EC_CODE; EndCode: SCROLLING_TEXT_LINE_EC_CODE; ContinuationType: ectEat),
     (StartCode: COMMENT_EC_CODE; EndCode: COMMENT_CONTINUATION_EC_CODE; ContinuationType: ectEat),
     (StartCode: CONDITIONAL_BRANCH_IF_EC_CODE; EndCode: CONDITIONAL_BRANCH_END_EC_CODE; ContinuationType: ectFind),
     (StartCode: LOOP_EC_CODE; EndCode: LOOP_END_EC_CODE; ContinuationType: ectFind),
     (StartCode: SET_MOVEMENT_ROUTE_EC_CODE; EndCode: SET_MOVEMENT_ROUTE_SUBCOMMAND_EC_CODE; ContinuationType: ectEat),
     (StartCode: BATTLE_PROCESSING_EC_CODE; EndCode: BATTLE_PROCESSING_END_EC_CODE; ContinuationType: ectSpecial),
     (StartCode: SHOP_PROCESSING_EC_CODE; EndCode: SHOP_PROCESSING_LINE_EC_CODE; ContinuationType: ectEat),
     (StartCode: SCRIPT_EC_CODE; EndCode: SCRIPT_LINE_EC_CODE; ContinuationType: ectEat)
  );

implementation

uses
  Graphics, Math, echelper, ec_base, newecformunit, clipboardhelper, globals,

  //debug-only
  Dialogs;

{$R *.lfm}

{ TECListFrame }

procedure TECListFrame.EcListBoxDrawItem(Control: TWinControl; Index: Integer;
  ARect: TRect; State: TOwnerDrawState);
var
  Phrase: TEcPhrase;
  CurrentX: Integer;
  I: Integer;
  Part: TEcPhrasePart;
  PartSize: TSize;
  Ec: TJSONObject;
  Indent: Integer;
  IndentJson: TJSONNumber;

  function GetLeaderWidth: Integer;
  var
    ColonSize: TSize;
  begin
    ColonSize := EcListBox.Canvas.TextExtent(':');
    GetLeaderWidth := Max(ColonSize.Width, EcListBox.ItemHeight);
  end;

  procedure DrawLeader;
    procedure DrawContinuationLeader;
    var
      ColonSize: TSize;
    begin
      with EcListBox.Canvas do begin
        ColonSize := TextExtent(':');

        if IsEcSelected(Index) then
          Font.Color := ColourScheme.GetSelTextColour(ecDefault)
        else
          Font.Color := ColourScheme.GetTextColour(ecDefault);

        TextOut(CurrentX + (LeaderWidth - ColonSize.Width - 6) div 2, ARect.Top + (EcListBox.ItemHeight - ColonSize.Height) div 2, ':');
      end;
    end;

    procedure DrawBeginningLeader;
    var
      Points: array [1..4] of TPoint;
      Padding: Integer;
      FullWidth: Integer;
      HalfWidth: Integer;
    begin
      Padding := 3;
      FullWidth := LeaderWidth - Padding * 2;
      HalfWidth := FullWidth div 2;

      with EcListBox.Canvas do begin
        Brush.Style := bsSolid;

        if IsEcSelected(Index) then
          Brush.Color := ColourScheme.GetSelTextColour(ecDefault)
        else
          Brush.Color := ColourScheme.GetTextColour(ecDefault);

        Points[1] := Point(CurrentX, ARect.Top + Padding + HalfWidth);
        Points[2] := Point(CurrentX + HalfWidth, ARect.Top + Padding);
        Points[3] := Point(CurrentX + FullWidth, ARect.Top + Padding + HalfWidth);
        Points[4] := Point(CurrentX + HalfWidth, ARect.Top + Padding + FullWidth);
        Polygon(@Points[1], 4);
      end;
    end;

  begin
    if IsFlipped then
      Dec(CurrentX, LeaderWidth + 1);

    if EcIsContinuation(Ec.Integers['code']) then
      DrawContinuationLeader
    else
      DrawBeginningLeader;

    if not IsFlipped then
      Inc(CurrentX, LeaderWidth + 1);
  end;

begin
  if (List = nil) or (Index >= List.Count) or (Index < 0) then
    Exit;

  Ec := List.Objects[Index];
  if (Ec <> nil) and Ec.Find('indent', IndentJson) then
    Indent := IndentJson.AsInteger;

  if LeaderWidth <= 0 then
    LeaderWidth := GetLeaderWidth;

  Phrase := Stringiser.StringiseEc(Ec);

  with EcListBox.Canvas do begin
    Pen.Style := psClear;
    Rectangle(ARect.Left, ARect.Top, ARect.Right, ARect.Bottom + 1);

    if IsFlipped then
      CurrentX := ARect.Right - EC_INDENT - Indent*INDENT_WIDTH
    else
      CurrentX := ARect.Left + EC_INDENT + Indent*INDENT_WIDTH;

    DrawLeader;


    Brush.Style := bsClear;

    for I := Low(Phrase) to High(Phrase) do begin
      Part := Phrase[I];
      PartSize := TextExtent(Part.Text);

      if IsFlipped then
        Dec(CurrentX, PartSize.Width);

      if IsEcSelected(Index) then
        Font.Color := ColourScheme.GetSelTextColour(Part.Colour)
      else
        Font.Color := ColourScheme.GetTextColour(Part.Colour);

      if Part.Colour <> ecInvisible then
        TextOut(CurrentX, ARect.Top + (EcListBox.ItemHeight - PartSize.Height) div 2, Part.Text);

      if not IsFlipped then
        Inc(CurrentX, PartSize.Width);
    end;
  end;
end;

procedure TECListFrame.EcListBoxMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  ScreenCoordinates: TPoint;

  procedure EnsureClickedIsSelected;
  var
    ListBoxCoordinates: TPoint;
    Index: Integer;
  begin
    ListBoxCoordinates := Point(X, Y);
    Index := EcListBox.ItemAtPos(ListBoxCoordinates, True);

    if (Index < 0) or (Index > EcListBox.Count) then
      Exit;

    if (Index >= SelectionStart) and
       (Index < SelectionStart + SelectionLength) then
      Exit;

    EcListBox.ClearSelection;
    EcListBox.ItemIndex := Index;
    EcListBox.Selected[Index] := True;
    EcListBoxSelectionChange(Sender, True);
  end;

begin
  if Button = mbRight then begin
    EnsureClickedIsSelected;
    SetUpPopupMenu;

    ScreenCoordinates := ClientToScreen(Point(X, Y));
    EcListPopupMenu.PopUp(ScreenCoordinates.X, ScreenCoordinates.Y);
  end;
end;

procedure TECListFrame.EcListBoxClick(Sender: TObject);
begin
  //TODO
end;

procedure TECListFrame.AddNewEcMenuItemClick(Sender: TObject);
begin
  AddNewCommand
end;

procedure TECListFrame.CopyEcMenuItemClick(Sender: TObject);
begin
  //TODO
end;

procedure TECListFrame.EcListBoxDblClick(Sender: TObject);
begin
  AddNewCommand
end;

procedure TECListFrame.EcListBoxSelectionChange(Sender: TObject; User: boolean);
  procedure UpdateSelection;
  var
    I: Integer;
  begin
    for I := SelectionStart to SelectionStart + SelectionLength -1 do begin
      EcListBox.Selected[I] := True;
    end;
  end;

  function GetFirstSelectedLine: Integer;
  var
    I: Integer;
  begin
    I := EcListBox.ItemIndex;
    while (I-1 >= 0) and EcListBox.Selected[I-1] do
      Dec(I);

    GetFirstSelectedLine := I;
  end;

  function GetUserSelectionLength: Integer;
  var
    I: Integer;
  begin
    I := 0;
    while (SelectionStart + I < List.Count) and EcListBox.Selected[SelectionStart + I] do
      Inc(I);

    GetUserSelectionLength := I;
  end;

begin
  if not User then
    Exit;

  if EcListBox.ItemIndex >= 0 then begin
    SelectionStart := GetSelectionStart(GetFirstSelectedLine);
    SelectionLength := GetSelectionLength(SelectionStart)
  end else
    SelectionLength := 0;

  UpdateSelection;
  SelectionLength := GetUserSelectionLength
  { TODO: make sure indent of the last command is same as of the first command }
end;

procedure TECListFrame.EditEcMenuItemClick(Sender: TObject);
begin
  EditSelectedCommand
end;

procedure TECListFrame.PasteEcMenuItemClick(Sender: TObject);
var
  PastedArray: TJSONArray;
  TypeString: String;
  StartIndex, I: Integer;

  IndentDifference, NewIndent: Integer;
begin
  TypeString := DataTypeStrings[DATA_TYPE_ID_EVENT_COMMAND];
  if ClipboardHasData(TypeString) then begin
    PastedArray := GetJsonArrayFromClipboard(TypeString);
    if (PastedArray <> nil) and (PastedArray.Count > 0) then begin
      if (SelectionStart >= 0) and (SelectionStart < List.Count) then
        StartIndex := SelectionStart
      else
        StartIndex := List.Count - 1;

       IndentDifference := List.Objects[StartIndex].Integers['indent']
                      - PastedArray.Objects[0].Integers['indent'];

       EcListBox.Items.BeginUpdate;

       for I := PastedArray.Count -1 downto 0 do begin
         List.Insert(StartIndex, PastedArray[I].Clone);
         NewIndent := List.Objects[StartIndex].Integers['indent']
                          + IndentDifference;
         List.Objects[StartIndex].Integers['indent'] := NewIndent;
         EcListBox.Items.Append('+');
       end;
       EcListBox.Items.EndUpdate;

       EcListBox.ClearSelection;
       SelectionStart := StartIndex;
       SelectionLength := PastedArray.Count;

       for I := SelectionStart to SelectionLength -1 do begin
         EcListBox.Selected[SelectionStart + I] := True;
       end;
    end;
  end;
end;

function TECListFrame.GetSelectionStart(MiddleIndex: Integer): Integer;
  function GetStarterEcCode(CurrentCode: Integer): Integer;
  var
    I: Integer;
  begin
    GetStarterEcCode := 0;
    for I := Low(StarterEcs) to High(StarterEcs) do begin
      if StarterEcs[I].ContinuationCode > CurrentCode then
        Exit;
      if StarterEcs[I].ContinuationCode = CurrentCode then begin
        GetStarterEcCode := StarterEcs[I].StartCode;
        Exit
      end;
    end;
  end;

var
  CurrentStart, StarterCode, StarterIndent: Integer;
  CurrentEc: TJSONObject;
  IsStarter: Boolean;

begin
  if (List = nil) or (MiddleIndex >= List.Count) then begin
    GetSelectionStart := MiddleIndex;
    Exit;
  end;

  CurrentStart := MiddleIndex;
  CurrentEc := List.Objects[MiddleIndex];
  StarterCode := GetStarterEcCode(CurrentEc.Integers['code']);
  StarterIndent := CurrentEc.Integers['indent'];
  IsStarter := False;

  if StarterCode <> 0 then
    while (CurrentStart >= 0) and not IsStarter do begin
      Dec(CurrentStart);
      CurrentEc := List.Objects[CurrentStart];
      IsStarter := (CurrentEc.Integers['code'] = StarterCode) and
                   (CurrentEc.Integers['indent'] = StarterIndent);
    end;

  GetSelectionStart := Max(0, CurrentStart);
end;

function TECListFrame.GetSelectionLength(StartingFrom: Integer): Integer;
  function GetEnderDesc(CurrentCode: Integer): PEnderEcDesc;
  var
    I: Integer;
  begin
    GetEnderDesc := nil;
    for I := Low(EnderEcs) to High(EnderEcs) do begin
      if EnderEcs[I].StartCode > CurrentCode then
        Exit;
      if EnderEcs[I].StartCode = CurrentCode then begin
        GetEnderDesc := @EnderEcs[I];
        Exit
      end;
    end;
  end;

var
  EnderDesc: ^TEnderEcDesc;
  CurrentEc: TJSONObject;
  StartIndent, NewLength: Integer;
  IsStopped: Boolean;
  EffectiveContinuation: TEcContinuationType;
begin
  GetSelectionLength := 1;
  if (List = nil) or (StartingFrom >= List.Count) then begin
    Exit;
  end;

  CurrentEc := List.Objects[StartingFrom];
  EnderDesc := GetEnderDesc(CurrentEc.Integers['code']);
  StartIndent := CurrentEc.Integers['indent'];
  NewLength := 1;
  IsStopped := False;

  if EnderDesc <> nil then begin
    EffectiveContinuation := EnderDesc^.ContinuationType;
    if (CurrentEc.Integers['code'] = BATTLE_PROCESSING_EC_CODE) and
       (CurrentEc.Arrays['parameters'].Booleans[2]
           or CurrentEc.Arrays['parameters'].Booleans[3]) then
      EffectiveContinuation := ectFind;

    while (StartingFrom + NewLength <= List.Count) and not IsStopped and
                          (EffectiveContinuation <> ectSpecial) do begin
      CurrentEc := List.Objects[StartingFrom + NewLength - 1];

      if EffectiveContinuation = ectFind then begin
        IsStopped := (CurrentEc.Integers['code'] = EnderDesc^.EndCode) and
                     (CurrentEc.Integers['indent'] = StartIndent);
      end;
      if EffectiveContinuation = ectEat then begin
         IsStopped := ((CurrentEc.Integers['code'] <> EnderDesc^.EndCode)
                       and ((CurrentEc.Integers['code'] <> EnderDesc^.StartCode)
                           or (NewLength <> 1)))
                     or (CurrentEc.Integers['indent'] <> StartIndent);
      end;

      Inc(NewLength);
    end;

    Dec(NewLength);
    if EnderDesc^.ContinuationType = ectEat then
      Dec(NewLength);
  end;

  GetSelectionLength := NewLength;
end;

function TECListFrame.IsEcSelected(Index: Integer): Boolean;
begin
  IsEcSelected := EcListBox.Selected[Index];
  {
  IsEcSelected := (Index >= SelectionStart) and
                  (Index < SelectionStart + SelectionLength);
  }
end;

function TECListFrame.IsSingleEcSelected: Boolean;
var
  OneCommandLength: Integer;
begin
  OneCommandLength := GetSelectionLength(SelectionStart);
  IsSingleEcSelected := OneCommandLength = SelectionLength;
end;

procedure TECListFrame.EditSelectedCommand;
var
  F: TECBaseFrame;
  FrameClass: TEcFrameClass;
  LengthChange, I: Integer;
begin
  //TODO: remove the test code
  FrameClass := GetEditorForEc(List.Objects[SelectionStart].Integers['code']);

  if FrameClass <> nil then begin
    F := FrameClass.Create(Self);
    F.SetUp(Db, nil, Troop);
    F.UpdateLanguage(EditorLanguage);
    if F.ShowWithExisting(List, SelectionStart) then begin

      LengthChange := F.GetLength - SelectionLength;
      if LengthChange <> 0 then begin
        EcListBox.Items.BeginUpdate;
        if LengthChange > 0 then
          for I := 1 to LengthChange do
            EcListBox.Items.Add('+')
        else
          for I := 1 to -LengthChange do
            EcListBox.Items.Delete(EcListBox.Items.Count -1);

        EcListBox.Items.EndUpdate;
      end;

      F.ReplaceEditedCommand(SelectionStart, SelectionLength);
      SelectionLength := F.GetLength;

      EcListBox.Refresh
    end;

    FreeAndNil(F);
  end;
end;

procedure TECListFrame.AddNewCommand;
var
  F: TECBaseFrame;
  FrameClass: TEcFrameClass;
  Indent, I, SelectedCode, EcLength: Integer;
begin
   NewECForm.SetDatabase(Db);
   SelectedCode := NewECForm.ShowSelection;
   if SelectedCode <> EC_SELECTION_CANCELLED then begin
     FrameClass := GetEditorForEc(NewECForm.SelectedCode);

     Indent := List.Objects[SelectionStart].Integers['indent'];

     if FrameClass <> nil then begin
       F := FrameClass.Create(Self);
       F.SetUp(Db, nil, Troop);
       F.UpdateLanguage(EditorLanguage);

       if F.ShowWithNew(Indent) then begin
         EcLength := F.GetLength;

         EcListBox.Items.BeginUpdate;
         for I := 1 to EcLength do
           EcListBox.Items.Add('+');

         EcListBox.Items.EndUpdate;

         F.AddNewCommand(List, SelectionStart);
         SelectionLength := EcLength;
       end;

       FreeAndNil(F);
     end else if NewECForm.SelectedCode = LOOP_EC_CODE then begin
         // TODO: replace here F.AddNewCommand with something else and then uncomment
         EcListBox.Items.Add('+');
         EcListBox.Items.Add('+');
         EcListBox.Items.Add('+');

         TECBaseFrame.AddCommandToList(TJSONArray.Create([
           TJSONObject.Create([
             'code', NewECForm.SelectedCode,
             'indent', Indent,
             'parameters', TJSONArray.Create()
           ]),
           TJSONObject.Create([
             'code', 0,
             'indent', Indent + 1,
             'parameters', TJSONArray.Create()
           ]),
           TJSONObject.Create([
             'code', LOOP_END_EC_CODE,
             'indent', Indent,
             'parameters', TJSONArray.Create()
           ])
         ]), List, SelectionStart);
         SelectionLength := 1;
       end else if IsCommandWithoutParameters(NewECForm.SelectedCode) then begin
         EcListBox.Items.Add('+');

         TECBaseFrame.AddCommandToList(TJSONArray.Create([
           TJSONObject.Create([
             'code', NewECForm.SelectedCode,
             'indent', Indent,
             'parameters', TJSONArray.Create()
           ])
         ]), List, SelectionStart);
         SelectionLength := 1;
       end
   end
end;

procedure TECListFrame.SetUpPopupMenu;
var
  ForSingleEc: Boolean;
begin
  ForSingleEc := IsSingleEcSelected;

  EditEcMenuItem.Enabled := ForSingleEc;
end;

procedure TECListFrame.SetDatabase(ADb: TDatabase);
begin
  Db := ADb;
  Troop := nil;

  {TODO: move colour scheme stuff out of here (perhaps into gameproject?)}
  ColourScheme := TEcLightColourScheme.Create;
  {TODO: move stringiser out of here???}
  Stringiser := TEcStringiser.Create;
  Stringiser.Db := Db;
  Stringiser.Troop := Troop;

  LeaderWidth := 0;
end;

procedure TECListFrame.SetTroop(ATroop: TJSONObject);
begin
  Troop := ATroop;
  Stringiser.Troop := Troop;
end;

procedure TECListFrame.SetList(AList: TJSONArray);
begin
  List := AList;
  if List <> nil then
    FillEventCommands;
end;

procedure TECListFrame.FillEventCommands;
var
  I: Integer;
begin
  { Lazarus doesn't seem to have lbVirtualOwnerDraw, so listboxes have to be
    either owner-drawn or virtual. We're choosing the first one. }
  with EcListBox.Items do begin
    BeginUpdate;
    Clear;

    for I := 0 to List.Count -1 do begin
      Add(IntToStr(I));
    end;
    EndUpdate;
  end;

end;

destructor TECListFrame.Destroy;
begin
  inherited Destroy;
  if ColourScheme <> nil then
    ColourScheme.Free;
  if Stringiser <> nil then
    Stringiser.Free;
end;

end.

